﻿
namespace Framework.Crypto.Ciphers
{
    /// <summary>
    /// Rc4 encoder/decoder.
    /// </summary>
    public class Rc4Cipher : ICipher
    {
        /// <summary>
        /// Generic Rc4 constructor with some byte array to manipulate.
        /// Uses key as a key.
        /// </summary>
        /// <param name="data">data to be encoded</param>
        /// <param name="key">key to be used</param>
        public Rc4Cipher(byte[] data, byte[] key)
            : base(data, key)
        {

        }

        /// <summary>
        /// Generic Rc4 constructor with some byte array to manipulate.
        /// Generates a new key with keyLength size.
        /// </summary>
        /// <param name="data">data to be encoded</param>
        /// <param name="keyLength">length of the key we need to generate</param>
        public Rc4Cipher(byte[] data, int keyLength)
            : base(data, keyLength)
        {

        }

        /// <summary>
        /// Encodes given byte array.
        /// </summary>
        /// <returns></returns>
        public override byte[] Encrypt()
        {
            byte[] s = new byte[256];
            byte[] k = new byte[256];
            byte[] resultData = new byte[data.Length];
            byte temp;
            int i, j;
            // initing
            for (i = 0; i < 256; i++)
            {
                s[i] = (byte)i;
                k[i] = key[i % key.Length];
            }
            // scrable
            j = 0;
            for (i = 0; i < 256; i++)
            {
                j = (j + s[i] + k[i]) % 256;
                temp = s[i];
                s[i] = s[j];
                s[j] = temp;
            }
            // encoding
            i = j = 0;
            for (int x = 0; x < data.Length; x++)
            {
                i = (i + 1) % 256;
                j = (j + s[i]) % 256;
                temp = s[i];
                s[i] = s[j];
                s[j] = temp;
                int t = (s[i] + s[j]) % 256;
                resultData[x] = (byte)(data[x] ^ s[t]);
            }
            return resultData;
        }

        /// <summary>
        /// Decodes given byte array.
        /// </summary>
        /// <returns></returns>
        public override byte[] Decrypt()
        {
            return Encrypt();
        }

        /// <summary>
        /// No clearence.
        /// </summary>
        /// <returns></returns>
        public override void Clear() {} // We'll have per-instance rc4 using, so no closing available. 
                                        // We'll better create IDisposable inheritance.
    }
}
